package com.unitd.modules.configcenter.component;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @desc 自定义组件,用于处理缓存
 * @filename CacheComponent.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date  2018-01-30
 */
@Component
public class CacheComponent implements DisposableBean {

	private Map<String, Object> cache = new ConcurrentHashMap<>();
	private PriorityBlockingQueue<CacheKey> cacheKeys = new PriorityBlockingQueue<>();
	private ScheduledExecutorService cleanScheduledExecutor = Executors.newScheduledThreadPool(1);

	/** 缓存时间(秒) */
	private static int defaultExpireTime = 300;
	/** 缓存池最大缓存记录条数 */
	private int maxSize = 5000;
	/** 当前缓存池的缓存记录数 */
	private AtomicInteger currentCacheSize = new AtomicInteger(0);

	public CacheComponent() {
		this(5000);
	}

	public CacheComponent(final long period, int maxSize) {
		this(period);
		this.maxSize = maxSize;
	}

	/**
	 * @desc 构造方法,处理缓存的添加和过期
	 * @param period 检查过期间隔（毫秒）
	 */
	public CacheComponent(final long period) {

		cleanScheduledExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				CacheKey cacheKey = cacheKeys.poll();
				if (cacheKey == null) {
					return;
				}

				long currentTimeMils = System.currentTimeMillis();
				if (cacheKey.expireAt > currentTimeMils) {
					// 放回去
					cacheKeys.add(cacheKey);
					return;
				}
				// 过期的移除
				cache.remove(cacheKey.key);
				currentCacheSize.decrementAndGet();

			}
		}, period, period, TimeUnit.MILLISECONDS);

	}

	/**
	 * @desc 设置缓存
	 * @param key 缓存key
	 * @param value 实际缓存的值
	 * @param timeout 缓存效期(单位秒)
	 * @param <T>
	 * @return
	 */
	public <T> boolean set(String key, T value, int timeout) {
		if (currentCacheSize.incrementAndGet() > maxSize) {
			throw new RuntimeException("当前缓存记录数已超出最大缓存记录数!");
		}

		cache.put(key, value);
		if (timeout > 0) {
			cacheKeys.add(new CacheKey(key, System.currentTimeMillis() + timeout * 1000));
		}
		return true;

	}

	/**
	 * @desc 根据缓存key从缓存池中获取缓存
	 * @param key 缓存key
	 * @param <T> 缓存对象
	 * @return 缓存对象
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(String key) {
		return (T) cache.get(key);
	}

	public boolean remove(String key) {
		Object removeObj = cache.remove(key);
		if (removeObj != null) {
			cacheKeys.remove(new CacheKey(key, 0));
			currentCacheSize.decrementAndGet();
		}
		return true;
	}

	/**
	 * @desc 判断缓存池中是否存在指定key的缓存
	 * @param key 缓存key
	 * @return
	 */
	public boolean exists(String key) {
		return cache.containsKey(key);
	}

	/**
	 * @desc 根据缓存key查询是否存在相应的缓存信息,如果不存在则直接将当前信息摄入缓存中
	 * @param key 缓存key
	 * @param dataCaller 缓存对象回调者
	 * @param <T> 缓存对象
	 * @return
	 */
	public <T> T queryTryCache(String key, Callable<T> dataCaller) {
		return queryTryCache(key, dataCaller, defaultExpireTime);
	}

	public <T> T queryTryCache(String key, Callable<T> dataCaller, int expire) {
		T result = get(key);
		if (result == null) {
			try {
				result = dataCaller.call();
				if (result != null) {
					set(key, result, expire);
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		} else {
		}
		return result;
	}

	private class CacheKey implements Comparable<CacheKey> {
		/** 缓存key */
		String key;
		/** 缓存效期, 毫秒 */
		long expireAt;

		public CacheKey(String key, long expireAt) {
			super();
			this.key = key;
			this.expireAt = expireAt;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((key == null) ? 0 : key.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			CacheKey other = (CacheKey) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (key == null) {
				if (other.key != null)
					return false;
			} else if (!key.equals(other.key))
				return false;
			return true;
		}

		private CacheComponent getOuterType() {
			return CacheComponent.this;
		}

		@Override
		public int compareTo(CacheKey o) {
			return Long.compare(this.expireAt, o.expireAt);
		}

		@Override
		public String toString() {
			return "[key=" + key + ", expireAt=" + expireAt + "]";
		}
	}

	/**
	 * @desc 销毁缓存线程
	 */
	@Override
	public void destroy() {
		cleanScheduledExecutor.shutdown();
	}
}